import 'dart:convert';

import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:kq_flutter_core_widget/utils/str_util.dart';

import '../../resources/l10n/kq_core_s.dart';

class BleNativeUtil {
  // 蓝牙操作通道
  static const _methodChannelName = 'flutter.keqiang.com/ble';
  static const _platformMethod = MethodChannel(_methodChannelName);

  // 蓝牙开关消息通道
  static const _eventEnableChannelName = 'flutter.keqiang.com/bleEnableEvent';

  // 蓝牙连接状态通道
  static const _eventConnectStateChannelName =
      'flutter.keqiang.com/bleConnectStateEvent';

  // 扫描结果通道
  static const _eventScanChannelName = 'flutter.keqiang.com/bleScanEvent';

  // 接收数据通道
  static const _eventNotifyChannelName = 'flutter.keqiang.com/bleNotifyEvent';

  // 扫描回调
  static final Map<String, BleScanCallback?> _scanCallbacks = {};

  // 数据回调
  static final Map<String, BleNotifyCallback?> _notifyCallbacks = {};

  // 蓝牙连接状态回调
  static final List<BleConnectStateCallback?> _connectStateCallbacks = [];

  // 蓝牙开关回调
  static final List<BleEnableCallback?> _enableCallbacks = [];

  // 扫描通道
  static final _platformScanEvent = const EventChannel(_eventScanChannelName)
    ..receiveBroadcastStream().listen((event) {
      if (event is! Map) {
        return;
      }

      var callback = _scanCallbacks[event['tag']];
      if (callback == null) {
        return;
      }
      bool isComplete = event['isComplete'] ?? false;
      int connectState = event['connectState'] ?? 0;
      String? name = event['name'];
      String? macAddress = event['macAddress'];
      int? rssi = event['rssi'];
      callback.call(
          isComplete,
          macAddress == null
              ? null
              : BleDevice(name, macAddress, rssi,
                  connectState: BleConnectState.valueToState(connectState)));
    });

  // 数据接收通道
  static final _platformNotifyEvent =
      const EventChannel(_eventNotifyChannelName)
        ..receiveBroadcastStream().listen((event) {
          if (event is! Map) {
            return;
          }

          String service = event['service'] ?? '';
          String character = event['character'] ?? '';
          var callback = _notifyCallbacks["${service}_$character"];
          if (callback == null) {
            return;
          }
          String value = event['value'] ?? '';
          List<int> valueDecode = base64Decode(value).toList();
          callback.call(service, character, valueDecode);
        });

  // 蓝牙连接状态通道
  static final _platformConnectStateEvent =
      const EventChannel(_eventConnectStateChannelName)
        ..receiveBroadcastStream().listen((event) {
          if (event is! Map) {
            return;
          }

          String? macAddress = event['macAddress'];
          int connectState = event['connectState'] ?? 0;

          for (var csl in _connectStateCallbacks) {
            csl?.call(macAddress, BleConnectState.valueToState(connectState));
          }
        });

  // 蓝牙开关消息通道
  static final _platformBleEnableEvent =
      const EventChannel(_eventEnableChannelName)
        ..receiveBroadcastStream().listen((event) {
          if (event is! Map) {
            return;
          }
          bool enable = event['enable'] ?? false;
          for (var cbk in _enableCallbacks) {
            cbk?.call(enable);
          }
        });

  /// 蓝牙是否打开
  static Future<bool?> isBleOpen() async {
    try {
      return await _platformMethod.invokeMethod('isBleOpen');
    } on PlatformException catch (e) {
      debugPrint(e.message);
    }
    return null;
  }

  /// 打开蓝牙
  /// 【注意】：Android 12及以上系统打开蓝牙需要[android.permission.BLUETOOTH_CONNECT]权限。
  static Future<bool?> openBle() async {
    try {
      return await _platformMethod.invokeMethod('openBle');
    } on PlatformException catch (e) {
      debugPrint(e.message);
    }
    return null;
  }

  /// 关闭蓝牙
  /// 【注意】：Android 12及以上系统无法通过第三方应用关闭蓝牙了。
  static Future<bool?> closeBle() async {
    try {
      return await _platformMethod.invokeMethod('closeBle');
    } on PlatformException catch (e) {
      debugPrint(e.message);
    }
    return null;
  }

  /// 开始扫描蓝牙
  /// [scanCallback] 扫描到设备回调
  /// [duration] 搜索时长，默认10秒，到时间自动停止扫码
  static Future<bool?> startScanBle(
      {required BleScanCallback scanCallback,
      Duration duration = const Duration(milliseconds: 10 * 1000)}) async {
    String tag = StrUtil.uuid();
    _scanCallbacks[tag] = scanCallback;

    /// 在 ios 不调用该方法是不会执行..receiveBroadcastStream
    _platformScanEvent;
    try {
      return await _platformMethod.invokeMethod(
          'startScanBle', {'tag': tag, 'duration': duration.inMilliseconds});
    } on PlatformException catch (e, stacktrace) {
      if (kDebugMode) {
        print(e);
        print(stacktrace);
      }
    }
    return null;
  }

  /// 停止扫码蓝牙
  static Future<bool?> stopScanBle() async {
    _scanCallbacks.clear();
    try {
      return await _platformMethod.invokeMethod('stopScanBle');
    } on PlatformException catch (e, stacktrace) {
      if (kDebugMode) {
        print(e);
        print(stacktrace);
      }
    }
    return null;
  }

  /// 连接蓝牙设备
  ///
  /// [device] 设备
  /// [stateCallback] 连接状态回调
  /// [timeout] 超时时间，默认10秒
  static Future<BleConnectResult?> connectDevice(
      {required BleDevice device,
      BleConnectStateCallback? stateCallback,
      Duration timeout = const Duration(milliseconds: 10 * 1000)}) async {
    /// 在 ios 不调用该方法是不会执行..receiveBroadcastStream
    _platformConnectStateEvent;
    if (stateCallback != null &&
        !_connectStateCallbacks.contains(stateCallback)) {
      _connectStateCallbacks.add(stateCallback);
    }
    try {
      var result = await _platformMethod.invokeMethod('connectDevice', {
        'macAddress': device.macAddress,
        'timeoutMills': timeout.inMilliseconds
      });
      var connected = result['connected'] ?? false;
      List<Object?>? services = result["services"];
      BleConnectResult entity = BleConnectResult(
        connected ? BleConnectState.connected : BleConnectState.disConnected,
      );
      List<BleGattService> servicesEntity = [];
      if (services != null) {
        for (var service in services) {
          if (service is Map) {
            String? uuid = service["uuid"];
            List<Object?>? characters = service["characters"];
            List<BleGattCharacter> charactersEntity = [];
            if (characters != null) {
              for (var character in characters) {
                if (character is Map) {
                  List<Object?>? descriptors = character["descriptors"];
                  String? uuid = character["uuid"];
                  List<BleGattDescriptor> descriptorsEntity = [];
                  if (descriptors != null) {
                    for (var descriptor in descriptors) {
                      if (descriptor is Map) {
                        String? uuid = descriptor["uuid"];
                        int? permission = descriptor["permission"];
                        String? value = descriptor["value"];
                        List<int>? valueDecode =
                            value == null ? null : base64Decode(value).toList();
                        descriptorsEntity.add(
                            BleGattDescriptor(uuid, valueDecode, permission));
                      }
                    }
                  }
                  charactersEntity
                      .add(BleGattCharacter(uuid, descriptorsEntity));
                }
              }
            }
            servicesEntity.add(BleGattService(uuid, charactersEntity));
          }
        }
      }
      device.services = servicesEntity;
      entity.services = servicesEntity;
      return entity;
    } on PlatformException catch (e, stacktrace) {
      if (kDebugMode) {
        print(e);
        print(stacktrace);
      }
    }
    return null;
  }

  /// 断开蓝牙设备
  ///
  /// [device] 设备
  static Future<bool?> disConnectDevice({required BleDevice device}) async {
    try {
      return await _platformMethod.invokeMethod('disConnectDevice', {
        'macAddress': device.macAddress,
      });
    } on PlatformException catch (e, stacktrace) {
      if (kDebugMode) {
        print(e);
        print(stacktrace);
      }
    }
    return null;
  }

  /// 开始监听特征值
  static Future<bool?> notify(
      {required BleDevice device,
      required String service,
      required String character,
      BleNotifyCallback? callback}) async {
    /// 在 ios 不调用该方法是不会执行..receiveBroadcastStream
    _platformNotifyEvent;
    if (callback != null) {
      String tag = "${service}_$character";
      _notifyCallbacks[tag] = callback;
    }
    try {
      return await _platformMethod.invokeMethod('notify', {
        'macAddress': device.macAddress,
        'service': service,
        'character': character,
      });
    } on PlatformException catch (e, stacktrace) {
      if (kDebugMode) {
        print(e);
        print(stacktrace);
      }
    }
    return null;
  }

  /// 取消监听特征值
  static Future<bool?> unNotify(
      {required BleDevice device,
      required String service,
      required String character}) async {
    try {
      return await _platformMethod.invokeMethod('unNotify', {
        'macAddress': device.macAddress,
        'service': service,
        'character': character,
      });
    } on PlatformException catch (e, stacktrace) {
      if (kDebugMode) {
        print(e);
        print(stacktrace);
      }
    }
    return null;
  }

  /// 写入数据
  static Future<bool?> writeCharacterData({
    required BleDevice device,
    required String service,
    required String character,
    required List<int> value,
  }) async {
    try {
      return await _platformMethod.invokeMethod('writeCharacterData', {
        'macAddress': device.macAddress,
        'service': service,
        'character': character,
        'value': value,
      });
    } on PlatformException catch (e, stacktrace) {
      if (kDebugMode) {
        print(e);
        print(stacktrace);
      }
    }
    return null;
  }

  /// 开启监听蓝牙开关信号
  /// [callback] 蓝牙开关回调
  static void listenBleEnable(BleEnableCallback callback) {
    /// 在 ios 不调用该方法是不会执行..receiveBroadcastStream
    _platformBleEnableEvent;
    if (!_enableCallbacks.contains(callback)) {
      _enableCallbacks.add(callback);
    }
  }

  /// 停止监听蓝牙开关信号
  /// [callback] 如果需要关闭同一个监听，需要传同一个callback对象
  /// [removeAll] 如果需要移除所有，传true，默认false
  static void stopListenBleEnable(
      {BleEnableCallback? callback, bool removeAll = false}) {
    if (removeAll) {
      _enableCallbacks.clear();
    } else {
      if (_enableCallbacks.contains(callback)) {
        _enableCallbacks.remove(callback);
      }
    }
  }
}

typedef BleScanCallback = Function(bool isComplete, BleDevice? device);

typedef BleNotifyCallback = Function(
    String service, String character, List<int> value);

typedef BleEnableCallback = Function(bool enabled);

typedef BleConnectStateCallback = Function(
    String? macAddress, BleConnectState connectState);

/// 蓝牙连接结果，包括特征值
class BleConnectResult {
  BleConnectState connectState;
  List<BleGattService>? services;

  BleConnectResult(this.connectState, {this.services});

  @override
  String toString() {
    return 'BleConnectResult{connectState: $connectState, services: $services}';
  }
}

/// GATT服务
class BleGattService {
  String? uuid;
  List<BleGattCharacter>? characters;

  BleGattService(this.uuid, this.characters);

  @override
  String toString() {
    return 'BleGattService{uuid: $uuid, characters: $characters}';
  }
}

/// GATT特征值
class BleGattCharacter {
  String? uuid;
  List<BleGattDescriptor>? descriptors;

  dynamic extraData;

  BleGattCharacter(this.uuid, this.descriptors, {this.extraData});

  @override
  String toString() {
    return 'BleGattCharacter{uuid: $uuid, descriptors: $descriptors, extraData: $extraData}';
  }
}

class BleGattDescriptor {
  /// Descriptor read permission
  static const int permissionRead = 0x01;

  /// Descriptor permission: Allow encrypted read operations
  static const int permissionReadEncrypted = 0x02;

  /// Descriptor permission: Allow reading with person-in-the-middle protection
  static const int permissionReadEncryptedMitm = 0x04;

  /// Descriptor write permission
  static const int permissionWrite = 0x10;

  /// Descriptor permission: Allow encrypted writes
  static const int permissionWriteEncrypted = 0x20;

  /// Descriptor permission: Allow encrypted writes with person-in-the-middle
  /// protection
  static const int permissionWriteEncryptedMitm = 0x40;

  /// Descriptor permission: Allow signed write operations
  static const int permissionWriteSigned = 0x80;

  /// Descriptor permission: Allow signed write operations with
  /// person-in-the-middle protection
  static const int permissionWriteSignedMitm = 0x100;

  String? uuid;

  List<int>? value;

  int? permission;

  BleGattDescriptor(this.uuid, this.value, this.permission);
}

/// 蓝牙设备
class BleDevice {
  /// 设备名称
  String? name;

  /// mac地址
  String? macAddress;

  /// 信号强度
  int? rssi;

  /// 连接状态
  BleConnectState? connectState;

  List<BleGattService>? services;

  BleDevice(this.name, this.macAddress, this.rssi,
      {this.connectState, this.services});

  @override
  String toString() {
    return 'BleDevice{name: $name, macAddress: $macAddress, rssi: $rssi, connectState: $connectState, services: $services}';
  }
}

/// 连接状态定义
enum BleConnectState {
  /// 已断开/未连接
  disConnected(0),

  /// 连接中
  connecting(1),

  /// 已连接
  connected(2),

  /// 断开中
  disConnecting(3);

  final int value;

  const BleConnectState(this.value);

  String getName() {
    switch (this) {
      case disConnected:
        return KqCoreS.currentResource.disConnected;
      case connecting:
        return KqCoreS.currentResource.connecting;
      case connected:
        return KqCoreS.currentResource.connected;
      case disConnecting:
        return KqCoreS.currentResource.disConnecting;
    }
  }

  // value 转 BleConnectState
  static BleConnectState valueToState(int value) {
    switch (value) {
      case 0:
        return BleConnectState.disConnected;
      case 1:
        return BleConnectState.connecting;
      case 2:
        return BleConnectState.connected;
      case 3:
        return BleConnectState.disConnecting;
    }
    return BleConnectState.disConnected;
  }
}
